package main

import (
	"bufio"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"unsafe"
)

// 全局变量
var flag bool = true // java boolean

// -128,127
var num1 int8 = 0 // java byte
var num1b byte = 0

// -32768,32767
var num2 int16 = 0 //java short
// -2147483648,2147483647
var num3 int32 = 0 // java int
var num3b rune = 0 //固定32位int
// -9223372036854775808,9223372036854775807
var num4 int64 = 0 // java long
var num4b int = 0  // 根据操作系统位数决定是32位还是64位

// 0,255
var num1a uint8 = 0

// 0,65535
var num2a uint16 = 0 // java char强转为int的范围类似
// 0,4294967295
var num3a uint32 = 0

// 0,18446744073709551615
var num4a uint64 = 0
var num4c uint = 0 // 根据操作系统位数决定是32位还是64位

// IEEE-754 32位浮点型数
var num5 float32 = 0.0 // java float
// IEEE-754 64位浮点型数
var num6 float64 = 0.0 // java double

// 32 位实数和虚数
var num7 complex64 = 0

// 64 位实数和虚数
var num8 complex128 = 0

var str string = "字符串"

// 批量定义
var (
	vname1 int32
	vname2 int16
)
var vname3, vname4, vname5 int8

// 常量，在程序运行时，不会被修改的量
const con1 int8 = 10
const (
	vname6 int8   = 11
	vname7 int16  = 12
	vname8 string = "常量字符串"
)
const vname9, vname10 int32 = 13, 14

// iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前)，const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
const (
	vname11 int32 = iota //0
	vname12 int32 = iota //1
	vname13 int32 = iota //2
)

/*
上述写法还可以简写成
const (

	vname11 = iota
	vname12
	vname13

)
*/
const vname14 = 15         //中间没出现iota，iota不会继续累加
const vname15 int32 = iota //3
/*
在定义常量组时，如果不提供初始值，则表示将使用上一行的表达式。
const (
    i=1<<iota//1<<0
    j=3<<iota//3<<1
    k//3<<2
    l//3<<3
)
i=1：左移 0 位，不变仍为 1。
j=3：左移 1 位，变为二进制 110，即 6。
k=3：左移 2 位，变为二进制 1100，即 12。
l=3：左移 3 位，变为二进制 11000，即 24。
*/

func main() {
	//var num9 int32 = 0//显式声明类型并赋值
	var num9 = 0 //声明并赋值
	num10 := 0   //短声明并赋值

	//批量赋值；1是int32，3是int16，2没有被赋值给任何变量，`_`下划线是个占位符用来指代一个虚空变量，虚空变量只可写入无法读取
	vname1, _, vname2 = 1, 2, 3

	//函数内局部变量；可以重名；函数内优先级更高会覆盖全局变量
	var flag bool = false // java boolean

	//内存地址；用于临时保存内存地址值，用于地址值计算；无法强转为指针类型;长度是8字节或4字节取决于操作系统位数
	var addr uintptr = uintptr(unsafe.Pointer(&num4))
	var addr2 *int64 = (*int64)(unsafe.Pointer(&num4))
	var addr3 uintptr
	//var addr3 *int = (*int)(addr)

	var dg1 int = 10
	var fl1 float64 = float64(dg1)

	var str1 string = "100"
	var dg2 interface{}
	var str2 interface{}
	var err1 error
	//字符串转整型；reflect.TypeOf(dg2)负责查询变量类型
	dg2, err1 = strconv.Atoi(str1)
	if err1 == nil {
		fmt.Printf("str = %s, reflect.TypeOf(dg2) = %v, reflect.TypeOf(dg2).Kind() = %v, reflect.TypeOf(dg2).Name() = %v, dg2 =  %d, \n", str1, reflect.TypeOf(dg2), reflect.TypeOf(dg2).Kind(), reflect.TypeOf(dg2).Name(), dg2)
	} else {
		fmt.Printf("字符串转换为整型过程出错！")
	}
	//整形转字符串
	str2 = strconv.Itoa(dg1)
	fmt.Printf("str2 = %s, reflect.TypeOf(str2) = %v, reflect.TypeOf(str2).Kind() = %v, reflect.TypeOf(str2).Name() = %v, dg1 =  %d, \n", str2, reflect.TypeOf(str2), reflect.TypeOf(str2).Kind(), reflect.TypeOf(str2).Name(), dg1)

	fmt.Printf("%f\n", fl1)
	/*
		%t bool true/false
		%d 整型变量 10进制
		%o 整型变量 8进制
		%x 整型变量 16进制
		%c 单个字符（根据 Unicode 码点）
		%U	Unicode 格式（码点和字符）
		%f 浮点型 6位小数
		%e 浮点型 科学记数法
		%g 根据数值自动选择%f或%e
		%v
			整数类型（如 int）：直接输出数值。
			浮点数类型（如 float64）：以小数点表示。
			字符串类型：直接输出字符串。
			结构体：输出结构体的字段值。
			数组、切片、map 等复合数据类型：输出其元素或键值对。
			通过 %+v 输出字段名+字段值。

		%p 内存地址类型 指针格式
		%T 用于输出变量的类型
		%s	字符串
		%q	带双引号的字符串
	*/
	fmt.Printf("%d,%d \n", num9, num10)
	fmt.Printf("%t %d %d %d %d %d %d %d %d %d %d %d %d %f %f %v %v %s %x %p \n", flag, num1, num1b, num2, num3, num3b, num4, num4b, num1a, num2a, num3a, num4a, num4c, num5, num6, num7, num8, str, addr, addr2)
	var result_str string = fmt.Sprintf("%t %d %d %d %d %d %d %d %d %d %d %d %d %f %f %v %v %s %x %p \n", flag, num1, num1b, num2, num3, num3b, num4, num4b, num1a, num2a, num3a, num4a, num4c, num5, num6, num7, num8, str, addr, addr2)
	fmt.Println(result_str)
	fmt.Printf("unsafe.Sizeof(uintptr) = %d", unsafe.Sizeof(addr3))
	fmt.Println()
	fmt.Printf("unsafe.Sizeof(string) = %d", unsafe.Sizeof(vname8)) //16 字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节，所以字符串类型大小为 16 个字节
	/*
		1.1 Go 的调试插件行为
		    在 VS Code 中运行 Go 程序时，Go 的调试插件（Go Debug Adapter 或 Delve）会对外部终端进行一些额外的处理。
		    当你设置 console: "externalTerminal" 时，Go 的调试插件会在程序运行结束后附加一个 等待用户输入的逻辑，以防止外部终端窗口立即关闭。
		    这种行为是 Go 调试插件 的特性，目的是为了让开发者有机会查看程序运行的输出。

		1.2 C 的调试插件行为
		    对于 C 语言，VS Code 使用的是 C/C++ 调试插件（Microsoft 提供的 C/C++ 扩展）。
		    C 的调试插件在运行程序时，不会对外部终端附加任何额外的行为。
		    因此，当 C 程序运行结束后，外部终端窗口会立即关闭，除非你在代码中显式添加 system("pause") 或其他等待逻辑。
	*/
	bufio.NewReader(os.Stdin).ReadBytes('\n') // 等待用户按下 Enter 键
}
